package ar.uba.dc.tl.mylanga.symbol;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

public class SymbolTable {

	private Symbol mark;
	private Stack<Symbol> symbols;
	private Map<Symbol, List<SymbolTableEntry>> table;

	public SymbolTable() {
		this.symbols = new Stack<>();
		this.table = new IdentityHashMap<>();
		this.mark = new SymbolFactory().get("Scope Delimiter");
	}

	public boolean contains(Symbol symbol) {
		assert symbol != null;

		return this.table.containsKey(symbol);
	}

	public SymbolTableEntry get(Symbol symbol) {
		assert symbol != null;

		if (this.table.containsKey(symbol)) {
			List<SymbolTableEntry> entries = this.table.get(symbol);
			SymbolTableEntry entry = entries.get(entries.size() - 1);

			return entry;
		}

		return null;
	}

	public void put(Symbol symbol) {
		assert symbol != null;

		this.symbols.push(symbol);
		this.ensureEntriesFor(symbol);
		List<SymbolTableEntry> entries = this.getEntriesFor(symbol);

		entries.add(new SymbolTableEntry(symbol));
	}

	public void createScope() {
		this.symbols.push(this.mark);
	}

	public void discardScope() {
		Symbol symbol = this.symbols.pop();

		while (symbol != this.mark) {
			this.removeEntry(symbol);

			symbol = this.symbols.pop();
		}
	}

	public Collection<Symbol> getSymbols() {
		return Collections.unmodifiableSet(this.table.keySet());
	}

	private void removeEntry(Symbol symbol) {
		List<SymbolTableEntry> entries = this.getEntriesFor(symbol);

		entries.remove(entries.size() -1);
        if (entries.size() == 0) {
            this.table.remove(symbol);
        }
	}

	private void ensureEntriesFor(Symbol symbol) {
		if (!this.table.containsKey(symbol)) {
			this.table.put(symbol, new ArrayList<SymbolTableEntry>());
		}
	}

	private List<SymbolTableEntry> getEntriesFor(Symbol symbol) {
		return this.table.get(symbol);
	}
}
